package com.yzqp.generator.util.resolve;

import com.yzqp.generator.exception.GenerateException;
import com.yzqp.generator.util.ParamMap;
import lombok.Data;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 1. Context环境角色
 * 其中，Context是环境角色，包含解释器之外的一些全局信息；AbstractExpression为抽象表达式，声明一个抽象的解释操作，
 * 这个接口为抽象语法树中所有的节点所共享；TerminalExression为终结符表达式，实现与文法中的终结符相关联的解释操作；
 * NonterminalExpression为非终结符表达式，为文法中的非终结符实现解释操作，对文法中每一条规则R1、R2……Rn都需要一个具体
 * 的非终结符表达式类。
 * <p>
 * 这个是解析的工具类
 * 这个工具类用的设计模式是解析器设计模式。
 */
@Data
public class ResolveContext {

    // 表达式的值
    private String expressionValue;

    // 模板所需的参数
    ParamMap paramMap = null;

    public ResolveContext(String expressionValue, Map map) {
        // 参数map
        ParamMap paramMap = new ParamMap(map);
        this.paramMap = paramMap;
        this.expressionValue = expressionValue;
    }

    public ResolveContext(String expressionValue, ParamMap paramMap) {
        this.paramMap = paramMap;
        this.expressionValue = expressionValue;
    }

    /**
     * 解析模板字符串
     *
     * @return
     */
    public String resolve() {
        String value = "";
        List<ResolveExpression> expressionList = ResolveContext.resolve(this.expressionValue, this.paramMap);
        for (ResolveExpression resolveExpression : expressionList) {
            try {
                value = value + resolveExpression.interpreter();
            }catch (Exception e){
                throw e;
            }
        }
        return value;
    }

    /**
     * 解析模板字符串
     *
     * @param expressionValue 字符串
     * @return 表达式集合
     */
    public static List<ResolveExpression> resolve(String expressionValue, ParamMap paramMap) {
        // 表达式结合
        List<ResolveExpression> expressionList = new ArrayList<>();
        // 没有处理的字符串
        String noDealString = expressionValue;
        // 已经处理过的字符串
        String alreadyDealString = "";
        // 正在处理的字符串
        String inDealString = "";
        // 解析字符串

        // 变量下标
        int paramIndex = noDealString.indexOf("$.{");
        // 函数下标
        int functionIndex = noDealString.indexOf("#.{start");
        // 有需要解析的变量
        while (paramIndex > -1 || functionIndex > -1) {

            // 函数 在 参数 的前面
            if (functionIndex != -1 && (paramIndex == -1 || (functionIndex < paramIndex))) {
                // A 寻找 函数的开始和结束的下标
                // A.1 开始的下标 即 #{for 的#的下标 (第一个函数的开始下标)
                int startIndex = noDealString.indexOf("#.{start");
                // A.2 寻找第一个函数结束的下标
                int endIndex = getEndFunctionIndex(startIndex, noDealString);

                // 重新修改开始结束标志 函数内容要包括 之前的换行符
                char[] cs = noDealString.toCharArray();
                for (int i = startIndex; i >= 0; i--) {
                    char c = cs[i];
                    if (c == '\n') {
                        startIndex = i - 1;
                        break;
                    }
                }


                // 根据for循环的开始结束下标 切割出 已经解析的字符串 还没有解析的字符串 要解析的字符串
                alreadyDealString = noDealString.substring(0, startIndex);
                // 新增一个字符串表达式
                expressionList.add(new StringTerminalResolveExpression(alreadyDealString, paramMap.cloneObject()));

                // 正要处理的字符串
                inDealString = noDealString.substring(startIndex, endIndex + 1);

                // 还未处理的字符串
                noDealString = noDealString.substring(endIndex + 1);

                // 如果是for函数
                if (inDealString.indexOf("#.{startfor") > -1) {
                    // 新增一个 for函数表达式
                    expressionList.add(new ForFunctionNonterminalResolveExpression(inDealString, paramMap.cloneObject()));

                    // if函数
                } else if (inDealString.indexOf("#.{startif") > -1) {
                    // 新增一个 if函数表达式
                    expressionList.add(new IfFunctionNonterminalResolveExpression(inDealString, paramMap.cloneObject()));
                }

                // 重新刷新 参数下标和函数下标
                paramIndex = noDealString.indexOf("$.{");
                functionIndex = noDealString.indexOf("#.{start");
            } else if(paramIndex != -1 && (functionIndex == -1 || (paramIndex < functionIndex))) {
                // 这是参数
                // A 寻找 函数的开始和结束的下标
                // A.1 开始的下标 即 #{for 的#的下标 (第一个函数的开始下标)
                int startIndex = noDealString.indexOf("$.{");
                // A.2 寻找第一个函数结束的下标
                int endIndex = noDealString.indexOf(".}", startIndex) + 1;

                // 根据for循环的开始结束下标 切割出 已经解析的字符串 还没有解析的字符串 要解析的字符串
                alreadyDealString = noDealString.substring(0, startIndex);
                // 新增一个字符串表达式
                expressionList.add(new StringTerminalResolveExpression(alreadyDealString, paramMap.cloneObject()));

                // 正要处理的字符串
                inDealString = noDealString.substring(startIndex, endIndex + 1);
                // 新增一个 变量表达式
                expressionList.add(new VariableTerminalResolveExpression(inDealString, paramMap.cloneObject()));

                // 还未处理的字符串
                noDealString = noDealString.substring(endIndex + 1);


                // 重新刷新 参数下标和函数下标
                paramIndex = noDealString.indexOf("$.{");
                functionIndex = noDealString.indexOf("#.{start");
            }else {
                // 没有需要再解析的数据了
                break;
            }


        }

        // 没有函数 没有变量 直接添加字符串表达式
        expressionList.add(new StringTerminalResolveExpression(noDealString, paramMap.cloneObject()));


        return expressionList;
    }

    /**
     * 获得对应开始函数的下标
     *
     * @param startIndex   函数的开始的位置
     * @param noDealString 需要解析的字符串
     * @return
     */
    public static int getEndFunctionIndex(int startIndex, String noDealString) {
        // 开始标志的函数的数量
        int startFlagCount = 0;
        // 函数结束标志的数量
        int endFlagCount = 0;
        // 最小的开始的函数下标
        int minStartIndex = startIndex + 1;
        // 最小的结束的函数的下标
        int minEndIndex = 0;
        // 结束标志 > 开始标志 结束条件
        while (startFlagCount >= endFlagCount) {
            int startFunctionIndex = noDealString.indexOf("#.{start", minStartIndex);
            int endFunctionIndex = noDealString.indexOf("end .}", minEndIndex);
            // 开始函数下标比较小
            if (startFunctionIndex == -1 && endFunctionIndex == -1) {
                throw new GenerateException("无法解析到结束函数标志 String : " + noDealString);
            } else if (startFunctionIndex == -1) {
                endFlagCount++;
                minEndIndex = endFunctionIndex + 1;
            } else if (endFunctionIndex == -1) {
                startFlagCount++;
                minStartIndex = startFunctionIndex + 1;
            } else if (startFunctionIndex < endFunctionIndex) {
                startFlagCount++;
                minStartIndex = startFunctionIndex + 1;
            } else if (startFunctionIndex > endFunctionIndex) {
                endFlagCount++;
                minEndIndex = endFunctionIndex + 1;
            }
        }
        // 把下标 + 1 扣回去
        int res = minEndIndex - 1;
        // 下标指向 end} 中的 e 结束标志要 + 3
        res = res + 5;
        return res;
    }

}
